// ************************************************************* //
//                                                               //
//        Copyright (C) 2015 by Socionext Inc.                   //
//        All rights reserved.                                   //
//        Socionext Inc. Proprietary and Confidential.           //
//                                                               //
// ************************************************************* //
/*****************************************************************************/
/**
* @file em_dma.c
* @brief DMA function
* @note None
* @attention None
*
*/
/*****************************************************************************/

// include -------------------------------------
// #include "common.h"
#include "em_int_cmn.h"
#include "em_config.h"
#include "em_host.h"
#include "em_dma.h"
#include "em_data.h"

#undef FILEID
#define FILEID FILE_EM_DMA

/// ADMA descriptor sub buffer
EM_DMA_SUBBUFFER s_SubBuffers[ EM_CONFIG_SUB_BUFFERS_COUNT ] __attribute__((aligned(32)));

static unsigned long em_DMA_createADMADescriptors(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest,
	EM_DMAMODE e_ADMAType,
	EM_DMA_SUBBUFFER *pSubBuffers,
	unsigned long ul_SubBuffersCount
);

static unsigned long em_DMA_freeDecsriptors(
	EM_REQUEST_PARAM* pRequest
);

static unsigned long em_DMA_setAddress(
	EM_SLOT* pSlot,
	void *pBufAddr
);

/*****************************************************************************/
/**
 * @brief get the DMA transfer mode
 * @param[in] pSlot
 * @param[in] pRequest
 * @return EM_DMA_NONEDMA_MODE Standard DMA mode
 * @return EM_DMA_ADMA2_MODE Advanced DMA Mode Version 2
 */
/*****************************************************************************/
EM_DMAMODE em_DMA_specTransMode(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest
){
	EM_DMAMODE e_Mode = EM_DMA_NONEDMA_MODE; // MANUAL
	unsigned long ul_srs16 = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return	e_Mode;
	}
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return	e_Mode;
	}

	/**
	 * Process start
	 */
	if (pSlot->eDmaMode != EM_DMA_AUTO_MODE)
	{
		return pSlot->eDmaMode;
	}

	// SRS16
	ul_srs16 = EM_READ_REG( pSlot->pRegSRS->SRS16.word) ;

	// ADMA2 support
	if (ul_srs16 & SRS16_ADMA2_SUPPORT)
	{
		// ADMA2
		e_Mode = EM_DMA_ADMA2_MODE;
	}
	return e_Mode;
}

/*****************************************************************************/
/**
 * @brief DMA transfer
 * @param[in] pSlot
 * @param[in] pRequest
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/

unsigned char em_DMA_prepareTransfer(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest
){
	EM_DMAMODE e_DMAMode = EM_DMA_NONEDMA_MODE; 
	unsigned long ul_CurSubsize = 0x10000uL;
	unsigned long ul_Ret = 0;
	unsigned long ul_Cnt = 0;
	unsigned long ul_srs10 = 0;
	unsigned long ul_DataSize = 0 ;
	unsigned long ul_Size = 0 ;
	unsigned long ul_BufAdr = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	e_DMAMode = em_DMA_specTransMode(pSlot, pRequest);
	
	switch(e_DMAMode)
	{
	case EM_DMA_SDMA_MODE:
	case EM_DMA_ADMA1_MODE:
		//! not supported
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_DMAMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION ;

	case EM_DMA_ADMA2_MODE:
		ul_BufAdr = (unsigned long)pRequest->pDataBuffer ;
		ul_DataSize = pRequest->ul_blockCount * pRequest->us_blockLen;
		if (ul_DataSize >= ul_CurSubsize)
		{
			// Higher than 64Kbyte
			ul_Cnt = 0 ;
			ul_Size = ul_DataSize ;
			while( ul_Size > 0 )
			{
				// sub buffer count check
				if (ul_Cnt >= EM_CONFIG_SUB_BUFFERS_COUNT)
				{
					DEBUG_TRACE_INFO_32( 0, ul_Cnt );
					DEBUG_TRACE_INFO_32( 4, ul_DataSize );
					DEBUG_TRACE_INFO_32( 8, ul_BufAdr );
					DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
					return EM_ERR_INVALID_PARAMETER ;
				}

				if (ul_Size < ul_CurSubsize){
					ul_CurSubsize = ul_Size;
				}
				s_SubBuffers[ul_Cnt].address = ul_BufAdr;
				s_SubBuffers[ul_Cnt].size = ul_CurSubsize;

				ul_Size -= ul_CurSubsize;
				ul_BufAdr += ul_CurSubsize;
				ul_Cnt++;
			}
		} else {
			// Up to 64Kbyte
			s_SubBuffers[0].address = ul_BufAdr;
			s_SubBuffers[0].size = ul_DataSize;
			ul_Cnt = 1;
		}
		//! create ADMA descriptor table
		ul_Ret = em_DMA_createADMADescriptors(
			pSlot,
			pRequest,
			e_DMAMode,
			&(s_SubBuffers[0]),
			ul_Cnt);
		if ( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}

		// set the ADMA descriptor table address
		ul_Ret = em_DMA_setAddress(
			pSlot,
			pRequest->pAdmaDescriptorTable);
		if ( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}

		// SRS10
		ul_srs10 = EM_READ_REG( pSlot->pRegSRS->SRS10.word ) ;
		// SRS10.DMASEL - DMA Select
		ul_srs10 &= ~SRS10_DMA_SELECT_MASK;
		EM_WRITE_REG( pSlot->pRegSRS->SRS10.word, ul_srs10 | SRS10_DMA_SELECT_ADMA2) ;
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_DMAMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER ;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief create the ADMA descriptor table
 * @note 32bit address only
 * @param[in] pSlot
 * @param[in] pRequest
 * @param[in] e_ADMAType
 * @param[in] pSubBuffers
 * @param[in] ul_SubBuffersCount
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 * @return EM_ERR_TO_FEW_MEM
 */
/*****************************************************************************/
static unsigned long em_DMA_createADMADescriptors(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest,
	EM_DMAMODE e_ADMAType,
	EM_DMA_SUBBUFFER *pSubBuffers,
	unsigned long ul_SubBuffersCount
){
	unsigned long ul_NumberOfDescriptos = ul_SubBuffersCount;
	unsigned long ul_RealDescSize = 0;
	unsigned long ul_DescSize = 0;
	unsigned long ul_Cnt = 0;
	unsigned long ul_Cnt_Dst = 0;
	unsigned long ul_CntlNum = 0;
	unsigned long *pDescriptors = NULL;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pSubBuffers == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	pDescriptors = pSlot->pDescriptorBuffer ;

	switch( e_ADMAType )
	{
	case EM_DMA_ADMA1_MODE:
		//! ADMA1 not supported
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_ADMAType );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION ;

	case EM_DMA_ADMA2_MODE:
		// descriptor size is using for 32bit address
		ul_DescSize =  EM_ADMA2_SIZE_OF_DESCRIPTOR_32;
		break;
	
	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_ADMAType );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER ;
	}

	//! calculation of the descriptor table size
	ul_RealDescSize = ul_NumberOfDescriptos * ul_DescSize;
	if (ul_RealDescSize > EM_CONFIG_MAX_DESCR_BUFF_SIZE)
	{
		DEBUG_TRACE_INFO_32( 0, ul_RealDescSize );
		DEBUG_TRACE_INFO_32( 4, ul_NumberOfDescriptos );
		DEBUG_TRACE_INFO_32( 8, ul_DescSize );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_TO_FEW_MEM;
	}

	// fill descriptors
	for (ul_Cnt = 0; ul_Cnt < ul_NumberOfDescriptos; ul_Cnt++)
	{
		pDescriptors[ ul_Cnt_Dst ] =
				EM_ADMA2_DESCRIPTOR_LENGHT(pSubBuffers[ ul_Cnt ].size)
				| EM_ADMA2_DESCRIPTOR_TYPE_TRAN
				| EM_ADMA2_DESCRIPTOR_VALID ;
		ul_CntlNum = ul_Cnt_Dst ;
		ul_Cnt_Dst++;

		pDescriptors[ ul_Cnt_Dst ] = (pSubBuffers[ ul_Cnt ].address & 0xFFFFFFFFuL);
		ul_Cnt_Dst++;

	}
	// last descriptor finishes transmission
	pDescriptors[ ul_CntlNum ] |= EM_ADMA2_DESCRIPTOR_END;

	pRequest->pAdmaDescriptorTable = pDescriptors;

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief DMA int handler
 * @param[in] pSlot
 * @param[in] pRequest
 * @param[in] ul_srs12
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_DMA_handleInterrupt(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest,
	unsigned long ul_srs12
){
	EM_DMAMODE e_DMAMode = EM_DMA_NONEDMA_MODE;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	e_DMAMode = em_DMA_specTransMode(pSlot, pRequest);

	if ( pRequest->sRequestFlags.uc_dataPresent == EM_DISABLE )
	{
		return EM_OK;
	}

	if ((ul_srs12 & SRS12_TRANSFER_COMPLETE)
		|| (ul_srs12 & SRS12_ERROR_INTERRUPT)
		|| (ul_srs12 & SRS12_CARD_REMOVAL) 
		|| (ul_srs12 & SRS12_CARD_INSERTION))
	{
		if (e_DMAMode == EM_DMA_ADMA2_MODE)
		{
			em_DMA_freeDecsriptors( pRequest );
		}
	}
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief DMA error
 * @param[in] pSlot
 * @return None
 */
/*****************************************************************************/
void em_DMA_HandleADMAError(
	EM_SLOT* pSlot
){
#if 0
	unsigned long ui_srs21 = 0;
	unsigned long ui_srs22 = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return ;
	}

	/**
	 * Process start
	 */

	ui_srs21 = EM_READ_REG( pSlot->pRegSRS->SRS21.word );
	ui_srs22 = EM_READ_REG( pSlot->pRegSRS->SRS22.word );
	
	DEBUG_TRACE_INFO( 0, (unsigned char)(ui_srs21 & SRS21_ADMA_ERROR_LENGTH_MISMATCH));
	DEBUG_TRACE_INFO( 1, (unsigned char)(ui_srs21 & SRS21_ADMA_ERROR_STATE_MASK));
	// 4byte Align
	DEBUG_TRACE_INFO_32( 4, ui_srs22 );
	DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
#endif
}

/*****************************************************************************/
/**
 * @brief free the ADMA descriptor
 * @param[in] pRequest
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_DMA_freeDecsriptors(
	EM_REQUEST_PARAM* pRequest
){
	/**
	 * parameter check
	 */
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return	EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if (pRequest->pAdmaDescriptorTable)
	{
		pRequest->pAdmaDescriptorTable = NULL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set the ADMA address
 * @param[in] pSlot
 * @param[in] pBufAddr
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_DMA_setAddress(
	EM_SLOT* pSlot,
	void *pBufAddr
){
	/**
	 * parameter check
	 */
	if( pBufAddr == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	EM_WRITE_REG( pSlot->pRegSRS->SRS22.word, (unsigned long)pBufAddr ) ;

	return EM_OK;
}

